home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / asmbler.arc / NFIND.ASM < prev    next >
Assembly Source File  |  1988-11-19  |  38KB  |  878 lines

  1. COMMENT &
  2.  
  3. XFIND Filter
  4. Command
  5. -------------------------------------------------------------------------
  6. Purpose:        This filter  sends to  the  standard output  device  all
  7.                 lines from the filenames  specified in the command  line
  8.                 that contain  the  specified  string.   Letter  case  is
  9.                 IGNORED in the  comparisons,  unless an  exact  match is
  10.                 requested by the /E switch.  Command line parameters may
  11.                 be separated by one or more blanks, tabs, or commas.
  12.  
  13. Format:         XFIND [/C] [/E] [/N] [/V] "pat" [[d:][path]filename[.ext]...]
  14.  
  15. Type:           Internal        External
  16.                                   ***
  17.  
  18. Remarks:        The  search is restricted  to strings  found in a single 
  19.                 lines.  Patterns split  across line  boundaries will not 
  20.                 be recognized.
  21.  
  22.                 The /C parameter causes XFIND to display only a count of
  23.                 the number  of matching  occurrences of  string in  each
  24.                 file, without  displaying the  matching lines  from  the
  25.                 file.
  26.  
  27.                 The /E parameter requests an exact match with respect to
  28.                 letter case in the string.
  29.  
  30.                 The /N parameter causes the relative line number of each
  31.                 matching line to be displayed ahead of the line from the
  32.                 file.
  33.  
  34.                 The /V  parameter causes  all lines  NOT containing  the
  35.                 string to be displayed.
  36.  
  37.                 The  string must  be enclosed  in double  quotes.    Two
  38.                 quotes in succession are taken as a single quote.
  39.  
  40.                 Unlike FIND, global filename  characters ARE  allowed in
  41.                 the  filenames  or  extensions,  but  alas,  due to  DOS
  42.                 2.0 limitations, not in directory paths.
  43.  
  44. Examples:
  45.                 XFIND /C "equ" *.asm  ! Count the number of equ's
  46.                 XFIND "foobar" *.for,*.asm,*.pas
  47.                 xfind /e "Tel:" phone1 phone2 ph*.txt phone3
  48.  
  49. Error returns:  0 - success
  50.                 1 - empty pattern
  51.                 2 - command line incomplete 
  52.                 3 - one or more file open failures (some output may be
  53.                     produced)
  54.                 4 - file not found in wildcard expansion
  55.                 5 - file read error
  56.  
  57. -------------------------------------------------------------------------
  58.  
  59. The code has been developed from a disassembly of the IBM PC FIND Filter
  60. command.
  61.  
  62. The 8088 has  single instructions  for scanning  a string  for an  exact
  63. match or mismatch with a single character, but none for doing this  with
  64. translation.  Using translation turns one  of these instructions into  a
  65. 5-instruction loop with 5 entry/exit instructions, and the other becomes
  66. a 9-instruction loop with 4 entry/exit instructions.  A test in a  large
  67. file directory showed this routine to be about 15% slower than the  FIND
  68. utility (87 sec vs 77 sec), which is not an unacceptable overhead.
  69.  
  70. [03-Feb-84]     Nelson H.F. Beebe, University of Utah
  71. [01-Mar-84]     Brad Davis, Drafting Services Ltd, Salt Lake City, UT
  72.                 (changed code to allow use as .COM file and have big
  73.                 input buffer)
  74. &
  75.  
  76. include ascii.inc
  77. include dos.inc
  78.  
  79. MAXBUF  equ     58*1024         ; Buffer size - freely variable within
  80.                                 ; segment size limitation.  Original value
  81.                                 ; was 4096.  Code takes 3K, leave 3K for stack
  82.  
  83. assume  cs:findc,ds:findc,es:findc,ss:findc
  84. findc   segment para public 'code'
  85.  
  86.         org     100H            ; where a .COM file begins
  87. start:  jmp     find            ; jump around constant data
  88.  
  89.         db      "MAUlloa/Microsoft/V126"
  90. colon   db      ':',' '         ; 16
  91. linstr  db      '['             ; 18
  92. intstr  db      8 dup (0)       ; 19
  93.  
  94. ; Flags recording switch settings.  These should be consecutive bytes.
  95. ; A value FLAGSET means the flag is set, any other means not set
  96.  
  97. FLAGSET equ     -1              ; value signalling flag set
  98.  
  99. flags   equ     $               ; symbol used to index table
  100. notflg  db      0               ; /V
  101. cntflg  db      0               ; /C
  102. numflg  db      0               ; /N
  103. exactflg db     0               ; /E
  104.  
  105.  
  106. ; return code flags in order of increasing severity.  The highest
  107. ; met is returned
  108.  
  109. RC_OKAY equ     0               ; success
  110. RC_PATT equ     1               ; empty pattern
  111. RC_PARM equ     2               ; bad command line syntax
  112. RC_FILE equ     3               ; file open failure
  113. RC_NFND equ     4               ; file not found
  114. RC_READ equ     5               ; file read error
  115.  
  116. rc      db      RC_OKAY         ; return code
  117.  
  118.         db      0,0             ; 24,25
  119. matches dw                      ; 26
  120. lineno  dw                      ; 28
  121.  
  122. find:                           ; Begin main program
  123.         mov     rc,RC_OKAY      ; tentative return code
  124.         mov     ah,$DOS_GETVERSION
  125.         int     $DOS
  126.         cmp     al,2            ; version 2 or later?
  127.         jge     cont            ; jump if yes
  128.         mov     dx,offset baddos
  129.         mov     ah,$DOS_STROUT  ; print string
  130.         int     $DOS
  131.         xor     ax,ax           ; clear ax
  132.         mov     ah,$DOS_EXIT
  133.         int     $DOS
  134.  
  135. assume  cs:findc,ds:findc
  136.  
  137. cont:                           ; continue main program now that DOS 
  138.         mov     si,81h          ; Address in PSP of command line
  139.         call    skipsp          ; Skip leading spaces
  140.         or      bx,bx           ; Test skip result, non-zero at CR
  141.         jz      haveparam       ; jump if have command line data
  142.  
  143. cnterr:
  144.         mov     dx,offset numerr; error - need more parameters
  145.         mov     cl,numlen    ; message and message length in dx,cl
  146.         call    writedserr      ; write a message
  147.         mov     al,RC_PARM      ; return code
  148.         call    setrc
  149.         jmp     errexit         ; abort 
  150.  
  151. haveparam:
  152.         mov     ah,$DOS_SWITCH  ; get switchar (undocument internal call)
  153.         mov     al,$DOS_SWITCH_GET
  154.         int     $DOS
  155.         push    dx              ; save switch character
  156.  
  157. scanswitch:                     ; begin loop to scan for switches
  158.         lodsb                   ; get byte from [si] to al, increment si
  159.         cmp     al,' '          ; blank?
  160.         je      moresp          ; jump if so to flush more space
  161.         cmp     al,.HT          ; tab?
  162.         je      moresp          ; jump if so to flush more space
  163.         cmp     al,.CR          ; end-of-line?
  164.         je      cnterr          ; jump if so -- error - need more data
  165.         pop     dx              ; restore switch character
  166.         cmp     al,dl           ; switch character?
  167.         jne     scanpat         ; jump if not - now expect to find "pattern"
  168.         push    dx              ; save switch character again
  169.         lodsb                   ; get byte from [si] to al, increment si
  170.         cmp     al,' '          ; blank?
  171.         je      moresp          ; jump if so to flush more space
  172.         cmp     al,.HT          ; tab?
  173.         je      moresp          ; jump if so to flush more space
  174.         cmp     al,.CR          ; end-of-line?
  175.         je      cnterr          ; jump if so -- error - need more data
  176.         call    lcuc            ; convert to upper-case
  177.         mov     bx,offset notflg
  178.  
  179.         cmp     al,'C'          ; /C? (count matching lines only)
  180.         je      setcnt          ; jump if yes
  181.  
  182.         cmp     al,'E'          ; /E? (exact match)
  183.         je      setexact        ; jump if yes
  184.  
  185.         cmp     al,'N'          ; /N? (line numbers)
  186.         je      setnum          ; jump if yes
  187.  
  188.         cmp     al,'V'          ; /V? (invert search)
  189.         je      setnot          ; jump if yes
  190.  
  191.         mov     pattrn[0],al ; unrecognized switch character
  192.         mov     dx,offset prmerr; message
  193.         mov     cl,prmlen    ; and length
  194.         call    writedserr      ; go write it
  195.         mov     dx,offset pattrn; NUL
  196.         mov     cx,1            ; 1 character
  197.         call    writedserr      ; write the NUL 
  198.         mov     dx,offset crlf  ; and a CR LF
  199.         mov     cx,2
  200.         call    writedserr
  201.         jmp     scanswitch      ; continue switch scan
  202.  
  203. setcnt:                         ; /C found
  204.         mov     di,cntflg-flags ; offset in switch table
  205.         jmp     short setflg    ; go set it
  206.  
  207. setexact:
  208.         mov     di,exactflg-flags       ; offset in switch table
  209.         jmp     short setflg    ; go set it
  210.  
  211. setnum:                         ; /N found
  212.         mov     di,numflg-flags ; offset in switch table
  213.         jmp     short setflg    ; go set it
  214.  
  215. setnot:                         ; /V found
  216.         mov     di,notflg-flags ; offset in switch table
  217.                                 ; fall through to setflg
  218.  
  219. setflg:                         ; set the switch
  220.         mov     byte ptr [bx+di],FLAGSET
  221.         jmp     scanswitch      ; continue switch scan
  222.  
  223. moresp:                         ; flush white space in command line
  224.         dec     si              ; backup to space just found
  225.         call    skipsp          ; go skip some space
  226.         or      bx,bx           ; end-of-line reached?
  227.         jz      scanswitch      ; jump if not
  228.         jmp     cnterr          ; error - expected more data
  229.  
  230. scanpat:                        ; Begin code to scan "pattern"
  231.         cmp     al,'"'          ; Quote?
  232.         jne     nopat           ; Jump if not - error if no pattern
  233.         mov     di,offset pattrn; where to store the pattern
  234.         xor     cx,cx           ; clear count of pattern length
  235.  
  236. patch:  
  237.         lodsb                   ; get byte from [si] to al, increment si
  238.         cmp     al,.CR          ; end-of-line?
  239.         jne     stlpat          ; jump if not, still in pattern
  240.  
  241. nopat:                          ; here if pattern absent or incomplete
  242.         mov     dx,offset synerr; message
  243.         mov     cl,synlen    ; and length
  244.         call    writedserr      ; write it
  245.         mov     al,RC_PATT      ; return code
  246.         call    setrc
  247.         jmp     errexit         ; abort
  248.  
  249. stlpat:                         ; here if still in pattern
  250.         cmp     al,'"'          ; quote?
  251.         jne     stoch           ; jump if not to save character
  252.         lodsb                   ; get byte from [si] to al, increment si
  253.         cmp     al,'"'          ; pair of quotes?
  254.         je      stoch           ; yes, save only one of them
  255.         dec     si              ; no, must be end of pattern
  256.         mov     patlen,cx    ; save pattern length
  257.         or      cx,cx           ; was it zero?
  258.         jnz     havpat          ; jump if not, have valid pattern
  259.         mov     al,RC_PATT      ; return code for empty pattern
  260.         call    setrc
  261.         jmp     errexit         ; abort
  262.  
  263. stoch:                          ; store pattern character
  264.         stosb                   ; store byte in al at [di], increment di
  265.         inc     cx              ; count the character
  266.         jmp     patch           ; go back for more
  267.  
  268. havpat: 
  269.         call    skipsp          ; flush more white space
  270.         or      bx,bx           ; end-of-line yet?
  271.         jz      havfil          ; no, expect filename(s) now
  272.         mov     ax,$STDIN       ; no file names, so use standard input
  273.         jmp     doread          ; go begin the read of the file
  274.  
  275. havfil:                         ; Begin scan for filename(s)
  276.         call    clrcnt          ; go zero some counts
  277.         mov     di,offset filnam; where to store the filename
  278.         xor     cx,cx           ; clear the filename length count
  279.  
  280. filch:                          ; loop to collect the next filename
  281.         lodsb                   ; get byte from [si] to al, increment si
  282.         cmp     al,' '          ; blank?
  283.         je      filend          ; jump if so, have a filename
  284.         cmp     al,.HT          ; tab?
  285.         je      filend          ; jump if so, have a filename
  286.         cmp     al,.COMMA       ; comma?
  287.         je      filend          ; jump if so, have a filename
  288.         cmp     al,.CR          ; end-of-line?
  289.         je      filend          ; jump if so, have a filename
  290.         stosb                   ; must be filename character, save it
  291.         inc     cx              ; and count it
  292.         jmp     filch           ; loop back for more
  293.  
  294. filend:                         ; here when filename collected
  295.         dec     si              ; backup to terminator character after name
  296.         mov     byte ptr [di],.NUL ; replace terminator by NUL
  297.         push    si              ; save registers so we can scan for the
  298.         mov     fillen,cx    ; save name length
  299.         mov     dx,offset fildta
  300.         mov     ah,$DOS_SETDTA
  301.         int     $DOS            ; set new disk transfer area
  302.  
  303.         mov     cx,0            ; attribute flags - find only normal files
  304.         mov     dx,offset filnam        ; address of wild-carded filename
  305.         mov     ah,$DOS_FINDFIRST
  306.         int     $DOS            ; find first matching file
  307.         jmp     short file00    ; go check error return
  308.  
  309. filenext:                        ; here to find next matching file
  310.         mov     ah,$DOS_FINDNEXT
  311.         int     $DOS            ; find next matching file
  312.  
  313. file00:                         ; here to check error return of FINDFIRST
  314.                                 ; or FINDNEXT
  315.         jnb     filmak          ; jump if carry not set
  316.  
  317. ; find file failed -- test error code returned in ax
  318.  
  319. file01: cmp     ax,18           ; no more files?
  320.         jne     file02          ; jump if not "no more files" condition
  321.         jmp     nxtfil          ; no more files from this expansion
  322.  
  323. file02:
  324.         cmp     ax,2            ; file not found?
  325.         jne     file03          ; jump if other error
  326.         mov     al,RC_NFND      ; "file not found" error
  327.         call    setrc
  328.         jmp     nxtfil
  329.  
  330. file03:                         ; some other undocumented error
  331.         mov     al,RC_FILE
  332.         call    setrc
  333.         jmp     nxtfil        
  334.  
  335. filmak:                         ; here after matching file found
  336.         call    makefile        ; go attach path to name
  337.         mov     dx,offset fpath ; address of the full filename
  338.         mov     ah,$DOS_OPEN2
  339.         mov     al,$DOS_OPEN2_READ
  340.         int     $DOS            ; open the file for input
  341.         jnb     filopn          ; jump if carry not set, open okay
  342.         jmp     badfil          ; carry set, open failed
  343.  
  344. filopn:                         ; here after file opened (ax=handle)
  345.         push    ax              ; save file handle
  346.         mov     dx,offset seplin; address of separator line
  347.         mov     cl,seplen    ; and its length
  348.         xor     ch,ch           ; clear top half of cx
  349.         call    writestr        ; write separator line
  350.         mov     dx,offset fpath ; address of filename
  351.         mov     cx,fpalen       ; and its length
  352.         call    writestr        ; append filename to separator line
  353.         cmp     cntflg,FLAGSET  ; was /C specified?
  354.         je      nocrlf          ; jump if yes - will only write count 
  355.         mov     dx,offset crlf  ; else output CR LF
  356.         mov     cx,2
  357.         call    writestr
  358.  
  359. nocrlf: 
  360.         pop     ax              ; restore file handle
  361.  
  362. doread:                         ; here to read the open file
  363.         mov     bx,ax           ; file handle
  364.  
  365. nxtread:                        ; here for next block of file
  366.         mov     dx,offset buffer; where to put block
  367.         mov     cx,MAXBUF       ; how long it is
  368.         mov     ah,$DOS_READ2
  369.         int     $DOS            ; read the block
  370.         jnb     didread         ; jump if carry not set, read okay
  371.         jmp     badread         ; carry set on error
  372.  
  373. didread:
  374.         or      ax,ax           ; test count of bytes read
  375.         jnz     grepbuf         ; if non-zero, go do pattern match
  376.         cmp     cntflg,FLAGSET  ; end-of-file, was /C specified?
  377.         jne     nocnt           ; no, all done on this file
  378.         call    writecnt        ; yes, write the count of matches
  379.  
  380. nocnt:  
  381.         cmp     bx,$STDIN       ; input from standard input file?
  382.         jne     doclose         ; jump if not and close the file
  383.         jmp     nrmexit         ; yes, no need to close, and all done
  384.  
  385. doclose:                        ; here to close the file (handle in bx)
  386.         mov     ah,$DOS_CLOSE2
  387.         int     $DOS            ; close the file
  388.         jmp     filenext        ; and go for next one in wild list
  389.  
  390. grepbuf:                        ; here to search block for pattern
  391.         push    bx              ; save file handle
  392.         mov     bp,offset buffer; where the block is
  393.         mov     di,ax           ; count of bytes in block
  394.         cmp     ax,MAXBUF       ; block full?
  395.         jge     nopatch         ; jump if yes
  396.         mov     bx,bp           ; partial block, get address of block
  397.         cmp     byte ptr [bx+di-01],.LF ; is last character LF?
  398.         je      nopatch         ; yes
  399.         mov     byte ptr [bx+di],.CR    ; no, append CR LF at end of file
  400.         inc     di              ; when it is missing, since
  401.         mov     byte ptr [bx+di],.LF    ; the search algorithm expects this
  402.         inc     di              ; terminator
  403.  
  404. nopatch:
  405.         push    di              ; save count of bytes in block
  406.         push    bp              ; save block address
  407.         mov     dx,patlen       ; pattern length
  408.         dec     dx              ; pattern length - 1
  409.  
  410. nxtscn: 
  411.         inc     lineno          ; increment line number
  412.         pop     bp              ; block address
  413.         mov     di,bp
  414.         pop     cx              ; count of bytes in block
  415.         mov     bx,cx
  416.         mov     al,.LF
  417.         jcxz    nofulln         ; jump if no more bytes in block
  418.         repnz scasb             ; search for LF marking end-of-line
  419.         jne     nofulln         ; jump if no end-of-line in block
  420.         push    cx              ; save count of bytes remaining
  421.         push    di              ; and location of end-of-line
  422.         mov     cx,di           ; where LF is in block
  423.         sub     cx,bp           ; compute length of line
  424.         mov     bx,cx           ; save length of line
  425.         dec     cx              ; then reduce for CR
  426.         dec     cx              ; and LF
  427.         jcxz    notfnd          ; jump if line is empty
  428.         mov     di,bp           ; address of line in block
  429.  
  430. samln:                          ; Loop to scan single line for pattern
  431.         mov     si,offset pattrn
  432.         lodsb                   ; get byte from [si] to al, increment si
  433.         cmp     exactflg,FLAGSET; /E specified?
  434.         je      sam01           ; jump if yes
  435.         call    xnzscasb        ; search for pattern byte, ignoring case 
  436.         jmp     short sam02
  437. sam01:
  438.         repnz scasb             ; search for exact match with pattern byte
  439. sam02:  
  440.         jnz     notfnd          ; jump if byte not found
  441.         cmp     cx,dx           ; found byte, is line longer than pattern?
  442.         jb      notfnd          ; jump if not, not acceptable match
  443.         push    di              ; save block address
  444.         push    cx              ; and length of line
  445.         mov     cx,dx           ; remaining length of pattern
  446.         jcxz    didfnd          ; jump if pattern exhausted
  447.         cmp     exactflg,FLAGSET; /E specified?
  448.         je      sam03           ; jump if yes        
  449.         call    xzcmpsb         ; compare rest of pattern with current source
  450.         jmp     short sam04
  451. sam03:  
  452.         repz    cmpsb           ; compare for exact match
  453. sam04:  
  454.         je      didfnd          ; jump if found string match
  455.         pop     cx              ; no match, restore old count
  456.         pop     di              ; and source index
  457.         jmp     samln           ; and go continue source scan
  458.  
  459. didfnd:                         ; here when pattern found
  460.         pop     ax              ; discard block address
  461.         pop     ax              ; and line length on stack
  462.         cmp     notflg,FLAGSET  ; was /V specified?
  463.         jne     negnot          ; jump if not
  464.         jmp     nxtscn          ; pattern found and /V says list non-matching
  465.                                 ; lines, so go search on next line
  466.  
  467. notfnd:                         ; here when pattern not found on current line 
  468.         cmp     notflg,FLAGSET  ; was /V specified?
  469.         jne     nxtscn          ; jump if not
  470.  
  471. negnot:                         ; here when (/V AND pattern NOT found) OR
  472.                                 ; (NOT /V AND pattern found), i.e. matched 
  473.         cmp     cntflg,FLAGSET  ; was /C specified?
  474.         jne     nocount         ; jump if not
  475.         inc     matches         ; had /C, so increment pattern match count
  476.         jmp     nxtscn          ; and go search next line
  477.  
  478. nocount:                        ; here on match when line is to be printed
  479.         push    dx              ; save dx (remaining length of pattern)
  480.         cmp     numflg,FLAGSET  ; was /N specified?
  481.         jne     nolnnum         ; jump if not
  482.         call    writelnnum      ; had /N, write line number
  483.  
  484. nolnnum:                        ; here after possibly writing line number
  485.         mov     dx,bp           ; address of line in block
  486.         mov     cx,bx           ; length of line
  487.         call    writestr        ; go write matching line
  488.         pop     dx              ; restore dx
  489.         jmp     nxtscn          ; go search next line
  490.  
  491. nofulln:                        ; here when line split across blocks;
  492.                                 ; the input file pointer must be backed
  493.                                 ; up to the beginning of the current line 
  494.         mov     dx,bx           ; length of line
  495.         pop     bx              ; file handle
  496.         or      dx,dx           ; test line length
  497.         jz      nxtblock        ; jump if 0
  498.         neg     dx              ; -(length of line)
  499.         mov     cx,-1           ; (cx,dx) = file offset
  500.         mov     al,$DOS_LSEEK_CURRENT
  501.         mov     ah,$DOS_LSEEK
  502.         int     $DOS            ; reposition to beginning of current line
  503.         jc      badread         ; abort if lseek failed
  504.  
  505. nxtblock:
  506.         jmp     nxtread         ; go for next block
  507.  
  508. badread:                        ; here on lseek/read failure
  509.         mov     al,RC_READ      ; return code
  510.         call    setrc
  511.  
  512. bad01:
  513.         cmp     bx,$STDIN       ; reading standard input?
  514.         jne     bad02           ; jump if not 
  515.         jmp     nrmexit         ; yes, all done
  516.  
  517. bad02:
  518.         mov     ah,$DOS_CLOSE2  ; no, close the file
  519.         int     $DOS
  520.         mov     dx,offset rdderr; error message
  521.         mov     cl,rddlen    ; and length
  522.         jmp     dowrite         ; write message
  523.  
  524. badfil:                         ; here on open failure
  525.         mov     dx,offset fnderr; error message
  526.         mov     cl,fndlen    ; and length
  527.         mov     al,RC_FILE      ; return code
  528.         call    setrc
  529.  
  530. dowrite:                        ; here to print error message and file name
  531.         call    writeerr
  532.         call    writefil
  533.  
  534. nxtfil:                         ; here to find next file on command line
  535.         pop     si              ; address of last byte examined
  536.         call    skipsp          ; flush white space
  537.         or      bx,bx           ; end-of-line reached? 
  538.         jnz     nrmexit         ; yes, all done
  539.         jmp     havfil          ; no, go collect file name
  540.  
  541. setrc:                          ; routine to set return code from al
  542.         cmp     al,rc           ; higher than current one?
  543.         jbe     setrc1          ; jump if not
  544.         mov     rc,al           ; else save new one
  545. setrc1:
  546.         ret                     ; return to caller
  547.  
  548.  
  549. ; routine to build a full filename in fpath (terminated by NUL)
  550. ; from wild-carded path in filnam and filename in fname, since 
  551. ; FINDFIRST/FINDNEXT do not return a complete file spec
  552.  
  553. makefile:
  554.         push    ax              ; save registers
  555.         push    cx
  556.         push    si
  557.         push    di
  558.  
  559.         mov     di,offset fpath ; where full file spec is to go
  560.         mov     si,offset filnam; where wild card spec is
  561.         cld                     ; clear direction flag for positive increment
  562.         push    di              ; save separator address+1
  563.  
  564. ; Copy filnam (up to last non-NUL) into fpath, remembering location
  565. ; of last separator (:/\) so we can append fname to it.  DOS should
  566. ; have done this!!!  Note that this omission prevents wildcarding
  567. ; directory names, since there is no way to determine the directory
  568. ; name when only the file name is returned by FINDFIRST/FINDNEXT
  569.  
  570. make01:                         ; loop copying filnam into fpath
  571.         lodsb                   ; source byte to al, increment si
  572.         cmp     al,.NUL         ; end-of-string?
  573.         je      make03          ; yes, all done
  574.         stosb                   ; no, store byte in al to [di], increment di
  575.         cmp     al,':'          ; device separator?
  576.         je      make02          ; jump if separator
  577.         cmp     al,'\'          ; path separator?
  578.         je      make02          ; jump if so
  579.         cmp     al,'/'          ; alternate path separator?
  580.         je      make02          ; jump if so
  581.         jmp     short make01    ; loop for next character
  582.  
  583. make02:
  584.         pop     ax              ; discard old address
  585.         push    di              ; remember separator address (+1)
  586.         jmp     short make01    ; loop for more
  587.  
  588. make03:                         ; here at end-of-string.  fpath now has
  589.                                 ; copy of filnam (wild-carded spec)
  590.         pop     di              ; address of last separator+1
  591.         mov     si,offset fname
  592.  
  593. make04:                         ; loop copying fname into fpath after term. 
  594.         lodsb                   ; byte from [si] to al, increment si
  595.         stosb                   ; byte from al to [di], increment di
  596.         cmp     al,.NUL         ; end-of-string yet?
  597.         jne     make04          ; loop if more
  598.  
  599.         xor     cx,cx           ; clear count
  600.         mov     si,offset fpath ; address of fpath
  601.  
  602. make05: 
  603.         lodsb                   ; loop counting characters
  604.         cmp     al,.NUL         ; end-of-string yet?
  605.         je      make06          ; yes
  606.         inc     cx              ; no, count the character
  607.         jmp     short make05    ; loop for more
  608.  
  609. make06:
  610.         mov     fpalen,cx       ; save fpath length
  611.         pop     di              ; restore registers saved at entry
  612.         pop     si
  613.         pop     cx
  614.         pop     ax
  615.         ret                     ; return to caller
  616.  
  617. errexit:                        ; here for error exit
  618.         cmp     rc,RC_PARM      ; bad parameter?
  619.         je      erruse          ; yes, print usage message
  620.         cmp     rc,RC_PATT      ; bad
  621.         je      erruse          ; yes, print usage message
  622.         jmp     nrmexit         ; else just exit
  623.  
  624. erruse:                         ; here to print usage message
  625.         mov     dx,offset useerr; message address
  626.         mov     cl,uselen    ; and length
  627.         call    writedserr
  628.  
  629. nrmexit:                        ; here to exit to DOS when done
  630.         mov     al,rc           ; set return code
  631.         mov     ah,$DOS_EXIT
  632.         int     $DOS            ; exit to DOS
  633.  
  634. skipsp:                         ; routine to skip white space on command line
  635.         cld                     ; clear direction flag to increment si
  636.         xor     bx,bx           ; clear end-of-line flag returned in bx
  637.  
  638. havesp:                         ; loop skipping white space
  639.         lodsb                   ; get byte from [si] to al, increment si
  640.         cmp     al,' '          ; blank? 
  641.         je      havesp          ; yes, keep looping
  642.         cmp     al,.HT          ; tab?
  643.         je      havesp          ; yes, keep looping
  644.         cmp     al,.COMMA       ; comma
  645.         je      havesp          ; yes, keep looping
  646.         cmp     al,.CR          ; end-of-line?
  647.         jne     nocr            ; jump if not - hit non-white space character
  648.         mov     bx,ax           ; end-of-line hit, set return flag non-zero
  649. nocr:
  650.         dec     si              ; backup to last white space character 
  651.         ret                     ; return to caller with bx set to flag
  652.  
  653. clrcnt:                         ; routine to clear search counts
  654.         mov     byte ptr matches,0   ; clear match count
  655.         mov     byte ptr lineno,0    ; and line number
  656.         ret
  657.  
  658. writecnt:                       ; routine to write count
  659.         push    bx              ; save file handle
  660.         cmp     bx,$STDIN       ; standard input?
  661.         je      nocolon         ; jump if yes (no file name output)
  662.         mov     dx,offset colon ; no, follow file name by colon
  663.         mov     cx,2            ; string length
  664.         call    writestr        ; write the colon
  665.  
  666. nocolon:
  667.         mov     ax,matches      ; count of matches
  668.         mov     di,offset intstr; message
  669.         call    writeint        ; write the integer
  670.         mov     dx,offset intstr; message
  671.         call    writestr        ; write the message
  672.         mov     dx,offset crlf  ; and follow it by
  673.         mov     cx,2            ; a CR LF
  674.         call    writestr
  675.         pop     bx              ; restore the file handle
  676.         ret                     ; return to caller
  677.  
  678. writelnnum:                     ; here to write a line number
  679.         push    bx              ; save file handle
  680.         push    dx              ; and remaining pattern length
  681.         mov     ax,lineno       ; line count
  682.         mov     di,offset intstr; message
  683.         call    writeint        ; write the integer
  684.         mov     byte ptr [di],']'       ; follow it by left square bracket 
  685.         inc     cx
  686.         inc     cx              ; increment count by 2
  687.         mov     dx,offset linstr; address of string
  688.         call    writestr        ; write [nnnn] string
  689.         pop     dx              ; restore pattern length
  690.         pop     bx              ; and file handle
  691.         ret
  692.  
  693. writestr:                       ; routine to write string at ds:dx,length in cx
  694.         mov     bx,$STDOUT
  695.         mov     ah,$DOS_WRITE2
  696.         int     $DOS
  697.         ret
  698.  
  699. writeint:                       ; routine to format integer in ax to [di]
  700.         mov     bx,10
  701.         xor     cx,cx           ; clear count of bytes written
  702.  
  703. intdiv: 
  704.         inc     cx              ; increment count of bytes written
  705.         cmp     ax,bx           ; remainder < 10?
  706.         jb      intdone         ; jump if yes, have only single digit left
  707.         xor     dx,dx           ; clear dx (top half of 32-bit numerator)
  708.         div     bx              ; (ax,dx)/10 to ax, (ax,dx) mod 10 to dx
  709.         add     dl,'0'          ; make into ASCII digit
  710.         push    dx              ; save on stack
  711.         jmp     intdiv          ; keep collecting digits
  712.  
  713. intdone:
  714.         add     al,'0'          ; make into ASCII digit
  715.         push    ax              ; save on stack
  716.         mov     bx,cx           ; save count of digits
  717.  
  718. intsto:                         ; loop to pull digits from stack
  719.         pop     ax              ; get a digit
  720.         stosb                   ; store in target string
  721.         loop    intsto          ; loop while cx > 0
  722.         mov     cx,bx           ; count of digits
  723.         ret                     ; return to caller
  724.  
  725. writefil:                       ; routine to write file name and CR LF
  726.         mov     dx,offset fpath ; full filename
  727.         mov     cx,fpalen       ; and length
  728.         call    writeerr        ; write it
  729.         mov     dx,offset crlf  
  730.         mov     cx,2
  731.         call    writeerr        ; write CR LF
  732.         ret                     ; return to caller
  733.  
  734. writedserr:                     ; routine to write error message from ds:
  735.         call    writeerr
  736.         ret
  737.  
  738. writeerr:                       ; routine to write error message
  739.         xor     ch,ch
  740.         mov     bx,$STDERR
  741.         mov     ah,$DOS_WRITE2
  742.         int     $DOS
  743.         ret
  744.  
  745. lcuc:                           ; routine to convert al to upper-case
  746.         cmp     al,'a'          ; less than 'a'
  747.         jb      ok              ; yes, cannot be lower-case
  748.         cmp     al,'z'          ; greater than 'z'
  749.         jg      ok              ; yes, cannot be lower-case
  750.         and     al,not 20h      ; turn off lc bit
  751.  
  752. ok:     
  753.         ret                     ; return to caller
  754.  
  755. ;
  756. ; Begin translated equivalent of REPNZ   SCASB
  757. ;
  758. xnzscasb:
  759.         push    ax              ; save ax and
  760.         push    bx              ; bx during this loop
  761.         mov     bx, offset xtable
  762.         xlat    byte ptr xtable ; al <- xtable(al) - translated pattern byte
  763.         mov     ah,al           ; translated pattern byte
  764.  
  765. loop1:  
  766.         mov     al,es:byte ptr [di]     ; source byte
  767.         xlat    byte ptr xtable ; translate source byte in al
  768.         inc     di              ; point to next byte (like scasb does)
  769.         cmp     ah,al           ; source = pattern?
  770.         loopne  loop1           ; decrement cx and loop while no match
  771.  
  772.         pop     bx              ; restore bx
  773.         pop     ax              ; and ax
  774.         ret
  775. ;
  776. ; End translated equivalent of REPNZ   SCASB
  777. ;
  778.  
  779. ;
  780. ; Begin translated equivalent of REPZ CMPSB
  781. ;
  782. xzcmpsb:
  783.         push    ax              ; save ax and
  784.         push    bx              ; bx during this loop
  785.         mov     bx, offset xtable
  786.  
  787. loop2:  
  788.         mov     al,byte ptr [si];pattern byte
  789.         xlat    byte ptr xtable ; al <- xtable(al) - translated pattern byte
  790.         mov     ah,al           ; translated pattern byte
  791.         mov     al,es:byte ptr [di]     ; source byte
  792.         xlat    byte ptr xtable ; translate source byte in al
  793.         inc     si              ; point to next byte (like cmpsb does)
  794.         inc     di              ; point to next byte (like cmpsb does)
  795.         cmp     ah,al           ; source = pattern?
  796.         loope   loop2           ; decrement cx and loop while match
  797.  
  798.         pop     bx              ; restore bx
  799.         pop     ax              ; and ax
  800.         ret
  801. ;
  802. ; End translated equivalent of REPZ CMPSB
  803. ;
  804. xtable  equ     $       ; translation table mapping lower case into upper
  805. xbyte=0
  806.         rept    97      ; NUL .. "`" --> themselves
  807.         db      xbyte
  808. xbyte=xbyte+1
  809.         endm
  810. xbyte=65
  811.         rept    26      ; "a".."z" --> "A".."Z"
  812.         db      xbyte
  813. xbyte=xbyte+1
  814.         endm
  815. xbyte=123
  816.         rept    133     ; "["..0FFH --> themselves
  817.         db      xbyte
  818. xbyte=xbyte+1
  819.         endm
  820.  
  821. patlen  dw                                      ; 0332
  822. pattrn  db      128 dup (0)                     ; 0334
  823. fillen  dw                                      ; 03b4
  824. filnam  db      65 dup (0)                      ; 03b6
  825.         db      9 dup (0)
  826.  
  827. ; file disk transfer area for wild-card expansion
  828.         even
  829. fildta  db      21 dup (?)      ; reserved for DOS
  830.         db      0               ; attribute found - 0 means only normal files
  831.         dw      ?               ; file's time
  832.         dw      ?               ; file's date
  833.         dw      ?               ; low word of file size
  834.         dw      ?               ; high word of file size
  835. fname   db      13 dup (?)      ; file name (asciz string)
  836.  
  837. ; end of file disk transfer area
  838.  
  839. fpalen  dw      0               ; length of fpath (up to before NUL)
  840. fpath   db      65 dup (0)      ; space for expanded file name from dta
  841.  
  842.  
  843. baddos  db      "Incorrect DOS version$"        ; 00
  844. crlf    db      .CR,.LF                         ; 16
  845. numerr  db      "XFIND: Invalid number of parameters",.CR,.LF     ; 18
  846. numlen  db      numlen-numerr                   ; 3C
  847. synerr  db      "XFIND: Syntax error",.CR,.LF   ; 3D
  848. synlen  db      synlen-synerr                   ; 51
  849. fnderr  db      "XFIND: File not found "        ; 52
  850. fndlen  db      fndlen-fnderr                   ; 67
  851. rdderr  db      "XFIND: Read error in "         ; 68
  852. rddlen  db      rddlen-rdderr                   ; 7C
  853. prmerr  db      "XFIND: Invalid Parameter "     ; 7D
  854. prmlen  db      prmlen-prmerr                   ; 95
  855. useerr  db      "Usage: XFIND [/C] [/E] [/N] [/V] "
  856.         db      '"string"" [[d:][path]filename[.ext] [,]]',.CR,.LF
  857.         db      "/C : match count only",.CR,.LF
  858.         db      "/E : exact match with respect to letter case required",.CR,.LF
  859.         db      "/N : print line numbers of matching lines",.CR,.LF
  860.         db      "/V : invert match - display lines NOT containing pattern"
  861.         db      .CR,.LF
  862. uselen  db      uselen-useerr
  863. seplin  db      .CR,.LF,"---------- "           ; 96
  864. seplen  db      SEPLEN-SEPLIN                   ; A3
  865. if ($-findc) mod 256
  866.         org     ($-findc)+256-(($-findc) mod 256) ; Start buffer on 256n
  867.                                 ; boundary 
  868. endif
  869. buffer  EQU     THIS BYTE       ; There must be space for MAXBUF dup (?)
  870.                                 ; here, plus some more on top for the
  871.                                 ; stack.  This trick is used instead of
  872.                                 ; MAXBUF dup (?) because even with
  873.                                 ; initialization to (?), LINK creates a
  874.                                 ; .EXE file proportionate in size to
  875.                                 ; this value...baahhh!!!
  876. findc   ends
  877.         end     start
  878.